#include "Die.h"
#include "Subprogram.h"

#include <cstdio>

namespace dwarf {

Subprogram::Subprogram(bool &_rtn, Dwarf_Debug _debug, Dwarf_Die _die)
		: debug_(_debug),
		  offset_(0),
		  lowPc_(NULL),
		  highPc_(NULL) {
	Dwarf_Error error;
	int rtn = dwarf_dieoffset(_die, &offset_, &error);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		_rtn = false;
		return;
	}
	else if (rtn == DW_DLV_NO_ENTRY) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		_rtn = false;
		return;
	}
	Dwarf_Addr lowPc;
	rtn = dwarf_lowpc(_die, &lowPc, &error);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		_rtn = false;
		return;
	}
	else if (rtn == DW_DLV_NO_ENTRY) {
//		fprintf(stderr, "[WRN] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		_rtn = false;
		return;
	}
	lowPc_ = (void *) lowPc;
	rtn = Die::getHighPc(debug_, _die, lowPc_, highPc_);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		_rtn = false;
		return;
	}
	else if (rtn == DW_DLV_NO_ENTRY) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		_rtn = false;
		return;
	}
	_rtn = true;
	return;
}

Subprogram::Subprogram(const Subprogram &_subprogram)
		: debug_(_subprogram.debug_),
		  offset_(_subprogram.offset_),
		  lowPc_(_subprogram.lowPc_),
		  highPc_(_subprogram.highPc_) {
}

Subprogram::~Subprogram() {
}

Dwarf_Debug Subprogram::getDebug() const {
	return debug_;
}

RaiiDie Subprogram::getDie() const {
	Dwarf_Error error;
	RaiiDie die(debug_);
	int rtn = dwarf_offdie(debug_, offset_, &die.getPointer(), &error);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return die;
	}
	else if (rtn == DW_DLV_NO_ENTRY) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return die;
	}
	return die;
}

RaiiPointer<char *> Subprogram::getName() const {
	RaiiPointer<Dwarf_Die> die = getDie();
	if (die.getPointer() == NULL) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return RaiiPointer<char *>(debug_);
	}
	RaiiPointer<char *> name(debug_);
	int rtn = Die::getName(die.getPointer(), name);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return name;
	}
	else if (rtn == DW_DLV_NO_ENTRY) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return name;
	}
	return name;
}

const void *Subprogram::getLowPc() const {
	return lowPc_;
}

const void *Subprogram::getHighPc() const {
	return highPc_;
}

}
